// ///////////////////////////// MIT License //////////////////////////////////// //
//                                                                                //
// Copyright (c) 2010 David Zsolt Manrique                                        //
//                    david.zsolt.manrique@gmail.com                              //
//                                                                                //
// Permission is hereby granted, free of charge, to any person obtaining a copy   //
// of this software and associated documentation files (the "Software"), to deal  //
// in the Software without restriction, including without limitation the rights   //
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      //
// copies of the Software, and to permit persons to whom the Software is          //
// furnished to do so, subject to the following conditions:                       //
//                                                                                //
// The above copyright notice and this permission notice shall be included in     //
// all copies or substantial portions of the Software.                            //
//                                                                                //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    //
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  //
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN      //
// THE SOFTWARE.                                                                  //
//                                                                                //
// ////////////////////////////////////////////////////////////////////////////// //

//!
//! ffftool
//!
#include "structure_io.h"
#include "potential.h"
#include "fit.h"
#include "control_io.h"


int main(int argc, char** argv)
{ 
try
{ 
/*
	std::string text="";
text += "<xyz>\n";
text += "H2O_se.xyz\n";
text += "<\\xyz>\n";
text += "\n";
text += "<variable-name>\n";
text += "energy\n";
text += "<\\variable-name>\n";
text += "\n";
text += "<fit-field>\n";
text += "background const  *-472.042 \n";
text += "bond mors    *5.0 *2.6 *2.5     :  1,2 2,3\n";
//text += "#bond harm  *3.0    *1.53     :  1,3\n";
text += "angle harm   *0.5    *100.0     :  1,2,3\n";
text += "<\\fit-field>\n";
text += "\n";
text += "<levenberg-marquardt-fit>\n";
text += "max_it 5000\n";
text += "<\\levenberg-marquardt-fit>\n";

std::istringstream is(text);
*/
	io::arguments arg(argc,argv);
	io::switcharg<false>    help(arg,"-h","--help", "print help message");
	io::switcharg<false>    version(arg,"-v","--version", "print version info");
	//io::vararg<std::string> xyzout_fn (arg,"-o","--out","optimal xyz output (with -r)","");
	//io::switcharg<false>    relax(arg,"-r","--relax", "relax atomic configuration");
	io::switcharg<false>    minimum_bg(arg,"-b","--background", "add the minimum total energy to the background as fitting param.");
	io::posarg<std::string> input_fn(arg,"input file name", "");
	io::posarg<std::string> output_fn(arg,"output file name", "");
	
	if(*help)
	{
		arg.print_help(std::cout,"ffftool"); 
		return EXIT_SUCCESS;
	}
	if(*version)
	{
		std::cout << "version 0.6" << std::endl;
		return EXIT_SUCCESS;
	}
	
	// input output streams
	io::input_stream input(*input_fn);
	io::output_stream output(*output_fn);
	
	std::istream &is = input.stream();
	std::ostream &os = output.stream();

	std::vector<std::string> input_lines;
	io::load(is,"#",input_lines);
	

	varname_t varname;
	input_lines >> varname;

	xyzfilelist_t filelist; 
	input_lines >> filelist;

	std::vector<unitcell_t> unitcells;
	std::vector<real_type> energies;
	std::vector<position_t> positions;
	label_t label;
	position_t min_position;
	real_type min_energy;

        //std::cerr << "namiafazs";
        // if a group is not defined it gives segmentation fault! \Todo  !!
        
	load_xyz(varname,filelist,unitcells,energies,positions,label,min_position,min_energy);

        //std::cerr << "JJJJJJJJJJJJJO";
	potential_fit_list_t potential_list;
	input_lines >> potential_list;
        
        //std::cerr << potential_list;
        
	if(*minimum_bg) 
	{
		potential_list.insert(new background::constant_fit(min_energy,potential_list.params.size()));
		//! There is a huge performance leak : if one overwrites a potential with an equivalent one which
		//! has parameters in the parameter list then however the potential will be destroyed, but
		//! but the parameter is still in the list making the parameter vector bigger than neccesarry
		//! which is involved in an LU decomposition, therefore the performance loss is significant!
		potential_list.params.push_back(min_energy);
	}
        else
        {
            potential_list.insert(new background::constant_fit(min_energy,-1));
            //potential_list.insert(new backgrou);            
        }
	//os << potential_list;

	vectorx_type p;
	potential_list.get_parameter(p);

        //os << potential_list;

	fit_levenberg_marquardt fit;
	input_lines >>= fit;
	//os << fit;

	//os << min_energy; 

	os << "Initial potential list : " << std::endl;
	os << potential_list;
	os << std::endl << std::endl;
	
	fit(p,positions,energies,potential_list);
	os << fit;
	os << "Final potential list : " << std::endl;
	os << potential_list;
	//system("pause");


} catch(const char* message)
{
	std::cerr << message << std::endl;
	std::cerr << "(exit)" << std::endl;  //system("pause");
	return EXIT_FAILURE;
} 
catch(const std::exception & e)
{
	std::cerr << e.what() << std::endl;
	std::cerr << "(exit)" << std::endl; //system("pause");
	return EXIT_FAILURE;
}
	return EXIT_SUCCESS;
}

/*

    std::string var_name;
    input_interpreter::input_variable_name::get(input_lines,var_name);
    
    std::vector<std::vector<Eigen::Vector3d> > structure;
	std::vector<Eigen::Matrix3d> ucs;
    std::vector<double>  energy;
    xyz<Eigen::Vector3d> min_frame;
    double min_energy;
    input_interpreter::input_xyz::get(input_lines,var_name,structure,ucs,energy,min_frame,min_energy);
    
	//! TODO get these..
	std::map<std::string,std::set<int> > group;
	for(int i=1;i<min_frame.length();i++) 
	{		
		group[min_frame[i].label].insert(i);
		group[io::to<std::string,int>(i)].insert(i);
	}
	input_interpreter::input_group::get(input_lines,group);

	std::set<std::pair<int,int> > topology; 
	input_interpreter::input_topology::get(input_lines,topology);

	// fill the full potential
	full_potential full(min_energy);
	std::vector<double> params;
	input_interpreter::input_internal::get(input_lines,group,topology,params,full);
	Eigen::VectorXd p; 
	if( params.size() > 0 )  p = Eigen::VectorXd::Map(&params[0],params.size());
	else throw std::runtime_error("At least one parameter has to be marked for fitting : use * symbol!");
	
  
	// fitting
	double tau;
	double e1;
	double e2;
	int kmax;
	input_interpreter::input_levenberg_marquardt::get(input_lines,tau,e1,e2,kmax);
	fit_levenberg_marquardt fit(tau,e1,e2,kmax);
	fit(p,structure,energy,full);

	// output
	os << "<levenberg-marquardt-fit-output>" << std::endl;
	fit.write(os);
	os << "<\\levenberg-marquardt-fit-output>" << std::endl;
	os << std::endl;	

	os << "<initial-position>" << std::endl;
	for(int i = 1; i <= min_frame.length(); i++)
	{
		os << std::left << std::setw(6) << i << ' ';
		os << std::right << std::setw(18) << std::setprecision(10) << std::fixed << min_frame[i].coord.x() << ' ';
		os << std::right << std::setw(18) << std::setprecision(10) << std::fixed << min_frame[i].coord.y() << ' ';
		os << std::right << std::setw(18) << std::setprecision(10) << std::fixed << min_frame[i].coord.z() << ' ';
		os << std::endl;
	}
	os << "<\\initial-position>" << std::endl;
	os << std::endl;
	os << "<internal>" << std::endl;
	full.write(os,p);
	os << "<\\internal>" << std::endl; 
 

*/

